package fractal;

import fractal.fractal.Fractal;
import fractal.res.Resource;
import jinyilw.common.OSTools;
import jinyilw.swing.SwingTools;
import jinyilw.swing.SystemTools;
import jinyilw.swing.icon.LazyScaleIcon;
import jinyilw.swing.rlaf.ui.RRootPaneUI;

import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.plaf.RootPaneUI;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.io.Serial;
/**
 * 分形面板
 */
public class FractalPanel extends JPanel
{
	@Serial
	private static final long serialVersionUID = -1256692687886750600L;
	Fractal fractal;// 分形类型
	BufferedImage fractalImage;

	int MW, MH;// 图像大小
	int moveStep = 3, growStep = 2;// 可调整的分形参数
	double zoomFactor = 2;
	int x0 = 0, y0 = 0, x1 = 0, y1 = 0, mouse = 1;// 用于鼠标坐标,判断左键右键
	boolean leftZoom = true;// 是否左键放大，为false时左键可移动
	double rDxy;// 用于鼠标操作判断

	// 控制线程
	int core = 2;
	static volatile boolean threadGo = false;
	volatile int processSign;
	double scaleX, scaleY;// 用于鼠标当前位置缩放
	RRootPaneUI rUI;
	Window window;

	LazyScaleIcon[] wordImage = new LazyScaleIcon[10];// 文字信息
	static int wordI = 0;

	public void ensureOneThread()// 确保只有一个线程运行
	{
		if (threadGo)
		{
			threadGo = false;
			try
			{
				Thread.sleep(600);
			} catch (InterruptedException ie)
			{
				ie.printStackTrace();
			}
		}
	}

	public FractalPanel(JDialog dialog)
	{
		init();
		if (dialog != null)
		{
			RootPaneUI ui = dialog.getRootPane().getUI();
			if (ui instanceof RRootPaneUI)
				rUI = (RRootPaneUI) ui;
		}
	}

	private void init()
	{
		addMouseMotionListener(new ThisMouseMotionListener());
		addMouseListener(new ThisMouseListener());
		//		Toolkit.getDefaultToolkit().addAWTEventListener(keyListener,
		//				AWTEvent.KEY_EVENT_MASK);
		for (int i = 0; i < wordImage.length; i++)
		{
			wordImage[i] = Resource.getLazyScaleIcon("word/word" + i + ".png");
		}
	}

	private final ThisKeyListener keyListener = new ThisKeyListener();

	@Override
	public void addNotify()
	{
		super.addNotify();
		Window w = SwingTools.getWindow(this);
		if (w != null && w != window)
		{
			if (window != null)
				window.removeKeyListener(keyListener);
			w.addKeyListener(keyListener);
			window = w;
		}
	}

	public void setFractal(Fractal fr)
	{
		fractal = fr;
		MW = fr.getWidth();
		MH = fr.getHeight();
	}

	public void setControlParameter(int growStep, double zoomFactor, int core,
			boolean leftZoom)
	{
		this.growStep = growStep;
		this.zoomFactor = zoomFactor;
		this.core = core;
		this.leftZoom = leftZoom;
	}
	public void repaintFractal()
	{
		fractalImage = fractal.getFractalImage(fractalImage, core);
		repaint();
	}

	public void expandRightDownSide(int eX, int eY)
	{
		if (eX == MW && eY == MH)
			return;
		fractal.expandRightDownSide(eX, eY);
		MW = eX;
		MH = eY;
		repaintFractal();
	}

	@Override
	protected void paintComponent(Graphics g)
	{
		paintBack(g);
	}

	private void paintBack(Graphics g)
	{
		g.drawImage(fractalImage, 0, 0, null);
		if (wordImage[wordI] == null)
			return;
		int w = wordImage[wordI].getIconWidth();
		int h = wordImage[wordI].getIconHeight();
		int x = MW - w - 15;
		int y = MH - h - 15;
		if (wordImage[wordI] != null)
			g.drawImage(wordImage[wordI].getImage(), x, y, w, h, null);
	}

	@Override
	public void setBounds(int x, int y, int width, int height)
	{
		super.setBounds(x, y, width, height);
		if (fractal != null && width != 0 && height != 0)
			expandRightDownSide(width, height);
	}

	private class ThisMouseListener extends MouseAdapter
	{
		@Override
		public void mousePressed(final MouseEvent e)
		{
			ensureOneThread();
			rDxy = fractal.getDxy();
			processSign = 1;
			mouse = e.getButton();
			x0 = e.getX();
			y0 = e.getY();
			x1 = x0;
			y1 = y0;
			if (mouse == 1)
				mouseZoomInAction();
			else if (mouse == 3)
				mouseZoomOutAction();
		}

		@Override
		public void mouseReleased(final MouseEvent e)
		{
			stopAction();
			processSign = 2;
			if (mouse == 1 && leftZoom)
			{
				if (x1 > x0)
				{
					fractal.chooseNewAreaFractal(x0, y0, x1, y1);
					wordI++;
					wordI %= wordImage.length;
					drawFractal();
				} else if (rDxy == fractal.getDxy() && x0 > x1)
					moveBackward();
				if (!OSTools.isMac() && rUI != null && rUI.isTransparent())
					rUI.makeTransparent(true);
			}
		}

		@Override
		public void mouseClicked(final MouseEvent e)
		{
			stopAction();
			processSign = 2;
			if (rDxy == fractal.getDxy() && mouse == 3)
				moveForward();
			else if (mouse == 1)
			{
				if (x0 >= (MW - wordImage[wordI].getIconWidth() - 15)
						&& y0 >= (MH - wordImage[wordI].getIconHeight() - 15))
				{
					if (wordI == 12)
					{
						SystemTools.browseURI(
								"http://user.qzone.qq.com/821073285/blog/1371537491");
					} else if (wordI == 13)
					{
						SystemTools.browseURI(
								"http://user.qzone.qq.com/821073285/photo/V11AUdS91iFUL0");
					}
				}
			}
		}
	}

	private final Color shadowColor = new Color(0, 0, 0, 100);

	private class ThisMouseMotionListener extends MouseMotionAdapter
	{
		@Override
		public void mouseDragged(final MouseEvent e)
		{
			stopAction();
			if (mouse == 1)
			{
				if (leftZoom)
				{
					x1 = e.getX();
					if (x1 > x0)
					{
						Graphics gr = FractalPanel.this.getGraphics();
						y1 = y0 + (x1 - x0) * MH / MW;
						int w = x1 - x0, h = y1 - y0;
						if (!OSTools.isMac() && rUI != null
								&& rUI.isTransparent())
							rUI.makeTransparent(false);

						paintBack(gr);
						gr.setColor(shadowColor);
						gr.drawRect(x0 + 1, y0 + 1, w, h);
						gr.setColor(Color.white);
						gr.drawRect(x0, y0, w, h);
					}
				} else
				{
					x1 = e.getX();
					y1 = e.getY();
					int xx = (x1 - x0) / 6;
					int yy = (y1 - y0) / 6;
					if (xx > 0)
						moveRight(xx);
					else
						moveLeft(-xx);
					if (yy > 0)
						moveDown(yy);
					else
						moveUp(-yy);
				}
			} else if (mouse == 3)
			{
				x1 = e.getX();
				y1 = e.getY();
				int xx = (x1 - x0) / 6;
				int yy = (y1 - y0) / 6;
				if (xx > 0)
					moveRight(xx);
				else
					moveLeft(-xx);
				if (yy > 0)
					moveDown(yy);
				else
					moveUp(-yy);
			}
		}
	}

	private class ThisKeyListener implements KeyListener
	{
		@Override
		public void keyTyped(KeyEvent e)
		{
		}

		@Override
		public void keyPressed(KeyEvent e)
		{
			int keyCode = e.getKeyCode();
			if (keyCode == KeyEvent.VK_SPACE)
				stopAction();
			else if (keyCode == KeyEvent.VK_UP)
				moveUp(moveStep);
			else if (keyCode == KeyEvent.VK_DOWN)
				moveDown(moveStep);
			else if (keyCode == KeyEvent.VK_RIGHT)
				moveRight(moveStep);
			else if (keyCode == KeyEvent.VK_LEFT)
				moveLeft(moveStep);
			else if (keyCode == KeyEvent.VK_W)
				moveUpAction();
			else if (keyCode == KeyEvent.VK_S)
				moveDownAction();
			else if (keyCode == KeyEvent.VK_D)
				moveRightAction();
			else if (keyCode == KeyEvent.VK_A)
				moveLeftAction();
			else if (keyCode == KeyEvent.VK_F1)
				growAction();
			else if (keyCode == KeyEvent.VK_F2)
				removeAction();
			else if (keyCode == KeyEvent.VK_COMMA)
				moveBackward();
			else if (keyCode == KeyEvent.VK_PERIOD)
				moveForward();
			else if (keyCode == KeyEvent.VK_F3)
				zoomInAction();
			else if (keyCode == KeyEvent.VK_F4)
				zoomOutAction();
		}

		@Override
		public void keyReleased(KeyEvent e)
		{
		}

	}

	//	private class ThisKeyListener implements AWTEventListener
	//	{
	//		@Override
	//		public void eventDispatched(AWTEvent event)
	//		{
	//			if (event.getClass() == KeyEvent.class
	//					&& FractalPanel.this.isShowing())
	//			{
	//				KeyEvent keyEvent = (KeyEvent) event;
	//				if (keyEvent.getID() == KeyEvent.KEY_PRESSED)
	//				{
	//					if (keyEvent.getKeyCode() == KeyEvent.VK_SPACE)
	//						stopAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_UP)
	//						moveUp(moveSize);
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN)
	//						moveDown(moveSize);
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT)
	//						moveRight(moveSize);
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT)
	//						moveLeft(moveSize);
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_W)
	//						moveUpAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_S)
	//						moveDownAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_D)
	//						moveRightAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_A)
	//						moveLeftAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_F1)
	//						growAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_F2)
	//						removeAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_COMMA)
	//						moveBackward();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_PERIOD)
	//						moveForward();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_F3)
	//						zoomInAction();
	//					else if (keyEvent.getKeyCode() == KeyEvent.VK_F4)
	//						zoomOutAction();
	//				}
	//			}
	//		}
	//	}

	public void drawPartFractal(int r1, int r2, int c1, int c2)
	{
		fractal.calculatePartFractal(r1, r2, c1, c2);
		repaintFractal();
	}

	public void drawFractal()
	{
		fractal.calculateFractalInThread(core);
		repaintFractal();
	}

	public void drawNewNFractal(int preN)
	{
		fractal.calculateNewNFractal(preN);
		repaintFractal();
	}

	public void moveDown(int l)
	{
		if (l > 0)
		{
			fractal.moveDown(l);
			repaintFractal();
		}
	}

	public void moveUp(int l)
	{
		if (l > 0)
		{
			fractal.moveUp(l);
			repaintFractal();
		}
	}

	public void moveLeft(int l)
	{
		if (l > 0)
		{
			fractal.moveLeft(l);
			repaintFractal();
		}
	}

	public void moveRight(int l)
	{
		if (l > 0)
		{
			fractal.moveRight(l);
			repaintFractal();
		}
	}

	public void moveForward()
	{
		if (fractal.moveForward())
		{
			wordI = (int) (wordImage.length * Math.random());
			drawFractal();
		}
	}

	public void moveBackward()
	{
		if (fractal.moveBackward())
		{
			wordI = (int) (wordImage.length * Math.random());
			drawFractal();
		}
	}

	public void largerStep()
	{
		fractal.largerStep(zoomFactor);
		drawFractal();
	}

	public void smallerStep()
	{
		fractal.smallerStep(zoomFactor);
		drawFractal();
	}

	public void mouseLargerStep()
	{
		fractal.mouseLargerStep(scaleX, scaleY, zoomFactor);
		drawFractal();
	}

	public void mouseSmallerStep()
	{
		fractal.mouseSmallerStep(scaleX, scaleY, zoomFactor);
		drawFractal();
	}

	final long movePerFrameT = 30, aniPerFrameT = 125;// 动画时间
	long ct1 = 0, ct2 = 0, sleepT;

	public void mouseZoomInAction()
	{
		new Thread(() -> {
			threadGo = true;
			scaleX = (double) x0 / MW;
			scaleY = (double) y0 / MH;
			try
			{
				Thread.sleep(500);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			while (threadGo && processSign == 1)
			{
				ct1 = System.currentTimeMillis();
				mouseLargerStep();
				ct2 = System.currentTimeMillis();
				sleepT = aniPerFrameT - (ct2 - ct1);
				if (sleepT < 0)
					sleepT = 0;
				try
				{
					Thread.sleep(sleepT + 20);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void zoomInAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				processSign = 1;
				ct1 = System.currentTimeMillis();
				largerStep();
				ct2 = System.currentTimeMillis();
				sleepT = aniPerFrameT - (ct2 - ct1);
				if (sleepT < 0)
					sleepT = 0;
				try
				{
					Thread.sleep(sleepT + 20);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void mouseZoomOutAction()
	{
		new Thread(() -> {
			threadGo = true;
			scaleX = 1 - (double) x0 / MW;
			scaleY = 1 - (double) y0 / MH;
			try
			{
				Thread.sleep(500);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			while (threadGo && processSign == 1)
			{
				ct1 = System.currentTimeMillis();
				mouseSmallerStep();
				ct2 = System.currentTimeMillis();
				sleepT = aniPerFrameT - (ct2 - ct1);
				if (sleepT < 0)
					sleepT = 0;
				try
				{
					Thread.sleep(sleepT + 20);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void zoomOutAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				processSign = 1;
				ct1 = System.currentTimeMillis();
				smallerStep();
				ct2 = System.currentTimeMillis();
				sleepT = aniPerFrameT - (ct2 - ct1);
				if (sleepT < 0)
					sleepT = 0;
				try
				{
					Thread.sleep(sleepT + 20);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void moveDownAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				moveDown(moveStep);
				try
				{
					Thread.sleep(movePerFrameT);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void moveUpAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				moveUp(moveStep);
				try
				{
					Thread.sleep(movePerFrameT);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void moveLeftAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				moveLeft(moveStep);
				try
				{
					Thread.sleep(movePerFrameT);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void moveRightAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo)
			{
				moveRight(moveStep);
				try
				{
					Thread.sleep(movePerFrameT);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void growAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo && fractal.getMaxN() < 2500)
			{
				ct1 = System.currentTimeMillis();
				drawNewNFractal(fractal.getMaxN() + growStep);
				ct2 = System.currentTimeMillis();
				sleepT = 98 - (ct2 - ct1);
				if (sleepT < 0)
					sleepT = 0;
				try
				{
					Thread.sleep(sleepT);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void removeAction()
	{
		new Thread(() -> {
			ensureOneThread();
			threadGo = true;
			while (threadGo && fractal.getMaxN() > growStep)
			{
				drawNewNFractal(fractal.getMaxN() - growStep);
				try
				{
					Thread.sleep(100);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}).start();
	}

	public void stopAction()
	{
		threadGo = false;
	}
}
